/*
 * The MIT License
 *
 * Copyright 2012 Anonymous.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package pentomino.logic;

import java.util.Stack;

/**
 *
 * Represents a game logic.
 */
public class PentominoGame {
    
    // A game components:
    private Playground gameField;
    private int currentFigure;
    private boolean started;
    private boolean completed;
    private Stack<Integer> pasteActions;
    private int lastRemoved;
    
    private long currentMS;
    private int currentAC;
    
    private static int fcount = 12;
    public static final Figure[] FIGURES = {
        new Figure('f'),
        new Figure('i'),
        new Figure('l'),
        new Figure('n'),
        new Figure('p'),
        new Figure('t'),
        new Figure('u'),
        new Figure('v'),
        new Figure('w'),
        new Figure('x'),
        new Figure('y'),
        new Figure('z')        
    };
    
    /*============== Constructors ==============*/
    
    /**
     * Initializes a new game by default.
     */
    public PentominoGame()
    {
        this(Playground.FIELD4X15);
    }
    
    /**
     * Initializes a new game of the specified field type.
     * 
     * @param type a Playground type
     */
    public PentominoGame(int type)
    {
        gameField = new Playground(type);
        currentFigure = -1;
        completed = false;
        started = true;
        pasteActions = new Stack<Integer>();
        lastRemoved = -1;
        currentAC = 0;
        currentMS = 0;
    }
    
    /*============== Member access ==============*/
    
    /**
     * Gets a current game playground.
     * 
     * @return a game field 
     */
    public Playground getPlayground()
    {
        return gameField;
    }
    
    /**
     * Gets an elapsed time after game starts.
     * 
     * @return time in milliseconds
     */
    public long getElapsedTime()
    {
        return currentMS;
    }
    
    /**
     * Gets the current actions count.
     * 
     * @return number of actions 
     */
    public int getActionsCount()
    {
        return currentAC;
    }
    
    /**
     * Sets the current actions count.
     * 
     * @param value a new time value
     */
    public void setElapsedTime(long value)
    {
        currentMS = value;
    }
    
    /**
     * Sets the current actions count.
     * 
     * @param value a new actions count
     */
    public void setActionsCount(int value)
    {
        currentAC = value;
    }
    
    /*============== Methods ==============*/
    
    /**
     * Starts a new game.
     * 
     * @param type a new game field type 
     */
    public void startNew(int type)
    {
        gameField = new Playground(type);
        currentFigure = -1;
        completed = false;
        started = true;
        pasteActions.clear();
        lastRemoved = -1;
        currentMS = 0;
        currentAC = 0;
        
        for (int i = 0; i < fcount; i++)
        {
            FIGURES[i].reset();
        }
    }
    
    /**
     * Sets the number of the selected figure.
     * 
     * @param source a game figure
     */
    public void setCurrentFigure(Figure source)
    {
        if (source == null)
        {
            currentFigure = -1;
            return;
        }
        
        for (int i = 0; i < fcount; i++)
            if (FIGURES[i].is(source.getType()))
                currentFigure = i;
    }
    
    /**
     * Sets the number of the selected figure.
     * 
     * @param source a game figure type 
     */
    public void setCurrentFigure(char source)
    {
        currentFigure = -1;
        
        for (int i = 0; i < fcount; i++)
            if (FIGURES[i].is(source))
                currentFigure = i;
    }
    
    /**
     * Gets the selected figure.
     * 
     * @return a game figure
     */
    public Figure getCurrentFigure()
    {
        if (currentFigure >= 0 && currentFigure < 12)
            return FIGURES[currentFigure];
        else
            return null;
    }
    
    /**
     * Inserts the selected figure into a game field.
     * 
     * @param x x-coord in array (see the translations in the UI class)
     * @param y y-coord in array (see the translations in the UI class)
     */
    public void insertCurrent(int x, int y)
    {
        if (currentFigure < 0)
            return;
        
        gameField.insert(FIGURES[currentFigure], x, y);
        pasteActions.push(currentFigure);
        currentFigure = -1;
        
        started = false;
        
        check();
    }
    
    /**
     * Removes the selected figure on the game field.
     * 
     * @param x x-coord in array (see the translations in the UI class)
     * @param y y-coord in array (see the translations in the UI class)
     */
    public void removeAt(int x, int y)
    {
        char c = gameField.figureAt(x, y);
        
        gameField.remove(c);
        
        lastRemoved = -1;
        
        for (int i = 0; i < fcount; i++)
            if (FIGURES[i].is(c))
                lastRemoved = i;
        
        pasteActions.removeElement(lastRemoved);
    }
    
    /**
     * Tries to insert the figure in the specified position.
     * 
     * @param x x-coord in array (see the translations in the UI class)
     * @param y y y-coord in array (see the translations in the UI class)
     */
    public boolean canInsertTo(int x, int y)
    {
        if (currentFigure >= 0)
            return gameField.tryInsert(FIGURES[currentFigure], x, y);
        else
            return false;
    }
    
    /**
     * Cancels the previous action.
     */
    public void undo()
    {
        if (!pasteActions.isEmpty())
        {
            int n = pasteActions.pop();
            
            gameField.remove(FIGURES[n]);
        }
    }
    
    /**
     * Counts the last executed action.
     */
    public final void countAction()
    {
        currentAC++;
    }
    
    /**
     * Increases the elapsed time.
     * 
     * @param dtime a time in milliseconds 
     */
    public final void incElapsedTime(long dtime)
    {
        currentMS += dtime;
    }
    
    /**
     * Gets the current player scores.
     * 
     * @return an user score value 
     */
    public final long getScore()
    {
        if (currentMS != 0 && currentAC != 0)
        {
            long score;
            double index;

            index = 100.0 / currentMS;
            index += 0.1 / currentAC;

            score = (long)Math.round(index * Short.MAX_VALUE);
            score *= 10 - gameField.getType();
            
            return score;
        }
        else
            return 0;
    }
    
    /**
     * Checks the end of the game.
     */
    private void check()
    {
        completed = gameField.isFull();
    }
    
    /**
     * Gets the executed actions (inserted figures) state.
     * 
     * @return true if some figures was inserted in the game field
     */
    public boolean hasExecutedActions()
    {
        return !pasteActions.isEmpty();
    }
    
    /**
     * Gets the last inserted figure id.
     * 
     * @return a figure id
     */
    public int getLastInsertedId()
    {
        if (pasteActions.empty())
            return -1;
        else
            return pasteActions.peek();
    }
    
    /**
     * Checks the game starts.
     */
    public boolean hasStarted()
    {
        return started;
    }
    
    /**
     * Checks the game ends.
     */
    public boolean hasFinished()
    {
        return completed;
    }
    
    /**
     * Gets the last removed figure id.
     * 
     * @return a figure id
     */
    public int getLastRemovedId()
    {
        return lastRemoved;
    }
}
